package com.cndw.kungfu.model.pvp.war;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

import com.cndw.kungfu.config.Constants;
import com.cndw.kungfu.config.ConstantsConfig;
import com.cndw.kungfu.config.ConstantsLog;
import com.cndw.kungfu.domain.Player;
import com.cndw.kungfu.ext.LanguageLib;
import com.cndw.kungfu.ext.LogUtil;
import com.cndw.kungfu.ext.TimeProcesser;
import com.cndw.kungfu.model.Context;
import com.cndw.kungfu.model.CrossProxySerialize;
import com.cndw.kungfu.model.DataSerialize;
import com.cndw.kungfu.model.WarSerialize;
import com.cndw.kungfu.model.battle.Battle;
import com.cndw.kungfu.model.goods.domain.SubPops;
import com.cndw.kungfu.model.sys.ChatMain;
import com.cndw.kungfu.model.sys.LogServer;
import com.cndw.kungfu.vo.PlayerChangeVO;

public class WarData implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	/**
	 * 当前季节
	 */
	private int cycle;

	/**
	 * 报名列表
	 */
	private ConcurrentHashMap<Long, Integer> appPlayerIds = new ConcurrentHashMap<Long, Integer>();

	/**
	 * 淘汰赛列表
	 */
	private static transient List<WarVs> vsList = new LinkedList<WarVs>();
	/**
	 * 决赛列表
	 */
	private List<WarVs> orderVsList = new LinkedList<WarVs>();
	/**
	 * 胜出的玩家
	 */
	private ConcurrentHashMap<Integer, Map<Integer, Long>> winerMap = new ConcurrentHashMap<Integer, Map<Integer, Long>>();
	/**
	 * 我的下注.对应的玩家
	 */
	private ConcurrentHashMap<Long, WarBet> betEasyMap = new ConcurrentHashMap<Long, WarBet>();
	/**
	 * 下注列表
	 */
	private ConcurrentLinkedQueue<WarBet> bets = new ConcurrentLinkedQueue<WarBet>();

	/**
	 * 胜出列表
	 */
	private ConcurrentHashMap<Long, Integer> winIds = new ConcurrentHashMap<Long, Integer>();
	/**
	 * 玩家对应胜利的次数
	 */
	private Map<Long, Integer> winTimesMap = new ConcurrentHashMap<Long, Integer>();
	/**
	 * 每轮晋级还剩余多少人
	 */
	private static transient Map<Integer, Integer> emtNums = new HashMap<Integer, Integer>();
	/**
	 * 是否发过奖励
	 */
	private static transient Map<Long, Boolean> awardMap = new HashMap<Long, Boolean>();

	private int camType = 0;

	private int step = 0;
	/**
	 * 淘汰赛轮次
	 */
	private int emtRund;
	/**
	 * 是否可下注
	 */
	private boolean bet;

	private transient boolean haveDo = false;

	public WarData() {
		cycle = WarConfig.getCycle();
	}

	public synchronized void clear() {
		if (CrossProxySerialize.getInstance().getSingleBoss() == 0) {
			CrossProxySerialize.getInstance().setSingleBoss(getBoss());
		}
		appPlayerIds.clear();
		vsList.clear();
		orderVsList.clear();
		winerMap.clear();
		winIds.clear();
		betEasyMap.clear();
		bets.clear();
		winTimesMap.clear();
		emtNums.clear();
		awardMap.clear();
		step = 0;
		camType = 0;
		emtRund = 0;
		awardMap.clear();
		bet = false;
		LogUtil.getMain().debug("war clear " + cycle);
		cycle = WarConfig.getCycle();
		commitSync();
	}

	public static WarData getInstance() {
		return WarSerialize.getInstance().getWarData();
	}

	public void commitSync() {
		WarSerialize.getInstance().commitSync();
	}

	public void applay(long playerId) {
		appPlayerIds.put(playerId, 0);
	}

	public boolean isApplay(long playerId) {
		return appPlayerIds.containsKey(playerId);
	}

	public List<WarVs> getVsList() {
		return vsList;
	}

	public Iterator<WarVs> getVsIterator() {
		return vsList.iterator();
	}

	public int getStep() {
		return step;
	}

	public boolean isProm() {
		return TimeProcesser.getUnixTime() >= WarConfig.applyTime + WarConfig.eliminateBegin + emtRund * 300;
	}

	public int getCamType() {
		return camType;
	}

	public Iterator<WarVs> getOrderVsList() {
		return orderVsList.iterator();
	}

	public ConcurrentHashMap<Integer, Map<Integer, Long>> getWinerMap() {
		return winerMap;
	}

	public boolean isBet() {
		return bet;
	}

	/**
	 * 记录下注
	 * 
	 * @param sourceId
	 * @param targetId
	 * @param asset
	 */
	public void betAsset(int camType, long sourceId, long targetId, int asset) {
		WarBet warBet = new WarBet();
		warBet.setAsset(asset);
		warBet.setC(camType);
		warBet.setS(sourceId);
		warBet.setT(targetId);
		bets.add(warBet);
		betEasyMap.put(sourceId, warBet);
	}

	/**
	 * 获得我的下注
	 * 
	 * @param sourceId
	 * @return
	 */
	public WarBet betGet(long sourceId) {
		return betEasyMap.get(sourceId);
	}

	public Iterator<WarBet> getBets() {
		return bets.iterator();
	}

	public int getEmtNum(int round) {
		if (emtNums.containsKey(round)) {
			return emtNums.get(round);
		}
		return 0;
	}

	private void signFail(long playerId) {
		int faid = appPlayerIds.containsKey(playerId) ? appPlayerIds.get(playerId) : 0;
		faid++;
		appPlayerIds.put(playerId, faid);
		if (faid >= WarConfig.eliminateTimes) {
			winIds.remove(playerId);
		}
	}

	public synchronized void checkCycle() {
		cycle = cycle == 0 ? 1 : cycle;
		if (cycle < WarConfig.getCycle()) {
			clear();
			CrossProxySerialize.getInstance().stateWar(0, step, camType);
			CrossProxySerialize.getInstance().commitSync();
		}
	}

	public synchronized void runBoss() {
		haveDo = false;
		int diff = TimeProcesser.getUnixTime() - WarConfig.getSingleBegin();
		if (step == 0) {
			if (diff < WarConfig.eliminateBegin) {
				return;
			}
			if (appPlayerIds.size() < 10 && !ConstantsConfig.isLocalHost()) {
				step = 4;
				LogUtil.getMain().debug("game is not open ");
				WarSerialize.getInstance().commitSync();
				return;
			}
			processEliminate();
			step = 1;
			haveDo = true;
		}
		if (step == 1) {
			if (diff >= WarConfig.eliminateBegin + emtRund * WarConfig.emtOneTime) {
				camType = 32;
				processGot32();
				processPromotion();
				step = 2;
				haveDo = true;
			}
		} else if (step == 2) {
			if (prossVs()) {
				processPromotion();
				haveDo = true;
			}
		} else if (step == 4) {
			return;
		}
		if (haveDo) {
			CrossProxySerialize.getInstance().stateWar(0, step, camType);
			CrossProxySerialize.getInstance().commitSync();
			commitSync();
		}
	}

	private void processEliminate() {

		LogUtil.getMain().debug("processEliminate");
		LogUtil.getMain().debug("appPlayerIds:" + appPlayerIds.size());
		int autoJoin = 50;
		if (appPlayerIds.size() < autoJoin || ConstantsConfig.isLocalHost()) {
			List<Long> heroList = DataSerialize.getInstance().getChallengeData().getPlayers(1, autoJoin);
			for (int i = 0; i < heroList.size(); i++) {
				long hId = heroList.get(i);
				if (!appPlayerIds.containsKey(hId)) {
					Player target = Context.getPlayerServer().getByPlayerId(hId);
					if (target == null) {
						continue;
					}
					applay(hId);

					if (appPlayerIds.size() >= autoJoin) {
						break;
					}
				}
			}
		}

		winIds.putAll(appPlayerIds);
		while (winIds.size() > WarConfig.promotionMember) {
			emtRund++;
			assign();
			emtNums.put(emtRund, winIds.size());
			LogUtil.getMain().debug("emtRund:" + emtRund);
		}

		LogUtil.getMain().debug("appPlayerIds:" + appPlayerIds.size());
		LogUtil.getMain().debug("winIds:" + winIds.size());
		LogUtil.getMain().debug("vsList:" + vsList.size());
	}

	private void assign() {
		List<Long> xxIds = new ArrayList<Long>(winIds.keySet());
		Collections.shuffle(xxIds);

		Iterator<Long> it = xxIds.iterator();
		while (it.hasNext()) {
			Long sourceId = (Long) it.next();
			if (it.hasNext()) {
				long targetId = it.next();
				int win = battle(sourceId, targetId);

				vsList.add(new WarVs(sourceId, targetId, win));

				if (win == 2) {
					signFail(sourceId);
				} else {
					signFail(targetId);
				}
				if (winIds.size() <= WarConfig.promotionMember) {// 低于32名退出
					break;
				}
			} else {

			}
		}

	}

	private void processGot32() {

		Map<Integer, Long> tmpIds = new LinkedHashMap<Integer, Long>();
		List<Long> tmpWins = new ArrayList<Long>(winIds.keySet());
		Collections.shuffle(tmpWins);
		StringBuilder sb = new StringBuilder();
		Iterator<Long> it = tmpWins.iterator();
		int i = 0;
		while (it.hasNext()) {
			Long long1 = (Long) it.next();
			Player player = Context.getPlayerServer().getByPlayerId(long1);
			if (player != null) {
				if (sb.length() > 0) {
					sb.append("、");
				}
				sb.append(player.getPlayerName());
			}
			i++;
			tmpIds.put(i, long1);

		}
		winerMap.putIfAbsent(camType, tmpIds);

		ChatMain.systemMessage(LanguageLib.get("war.pro.chat32", sb));
		for (Long joinId : appPlayerIds.keySet()) {
			if (winIds.containsKey(joinId)) {
				continue;
			}
			if (awardMap.containsKey(joinId)) {
				continue;
			}
			sendFailAward(joinId);
		}
		
		
	}

	private void processPromotion() {
		if (winerMap.containsKey(camType)) {
			LogUtil.getMain().debug("camType:" + camType + " -> winerMap:" + winerMap.get(camType).size() + " " + orderVsList.size());

			Map<Integer, Long> winer = winerMap.get(camType);
			if (winer.size() == 1) {
				CrossProxySerialize.getInstance().setSingleBoss(getBoss());
				LogUtil.getMain().debug(" game is over ... boss is find ");
				step = 3;
				return;
			} else {
				// 分配比賽
				assignPromotion(winerMap.get(camType));
			}

		} else {
			LogUtil.getMain().debug("err .....");
			step = 3;
			return;
		}

	}
	
	private void sendFailAward(long uid){
		Player source = Context.getPlayerServer().getByPlayerId(uid);
		awardMap.put(uid, true);
		int heroNum = WarConfig.joinAward[0];
		int asset = WarConfig.joinAward[1];
		String title = LanguageLib.get("war.award.title");
		String content = LanguageLib.get("war.award.content4", camType,heroNum, asset);
		List<SubPops> attachment = new ArrayList<SubPops>();
		attachment.add(new SubPops(WarConfig.heroGoodsId, heroNum, 0, 0));
		attachment.add(new SubPops(0, 0, asset, 0));
		Context.getMessageServer().sendSysMail(source.getPlayerId(), source.getPlayerName(), title, content, attachment);
		LogServer.recordLog(source, ConstantsLog.activity, 90303, heroNum, asset, 0, 1);
	}

	private void sendAward(long uid, int camType) {
		Player source = Context.getPlayerServer().getByPlayerId(uid);
		awardMap.put(uid, true);
		int[] award = WarConfig.awardMap.get(camType);
		int heroNum = award[0];
		int asset = award[1];
		String title = LanguageLib.get("war.award.title");
		String content = LanguageLib.get("war.award.content", WarConfig.getName(camType), heroNum, asset);
		List<SubPops> attachment = new ArrayList<SubPops>();
		attachment.add(new SubPops(WarConfig.heroGoodsId, heroNum, 0, 0));
		attachment.add(new SubPops(0, 0, asset, 0));
		Context.getMessageServer().sendSysMail(source.getPlayerId(), source.getPlayerName(), title, content, attachment);
		LogServer.recordLog(source, ConstantsLog.activity, 90303, heroNum, asset, camType, 1);
		if (award[6]>0) {
			Context.getGeneralServer().addTitle(uid, award[6]);
		}		
	}

	private void proNext(int camType, long winId, int orderId, long lostId) {
		if (!winerMap.containsKey(camType)) {
			winerMap.putIfAbsent(camType, new LinkedHashMap<Integer, Long>());
		}
		if (winerMap.get(camType).containsKey(orderId)) {
			LogUtil.getMain().error("exits in here ." + orderId + "c: " + camType + " w:" + winId);
		}
		LogUtil.getMain().debug("camType " + camType + " orderId " + orderId + " winId " + winId);
		winerMap.get(camType).put(orderId, winId);
		if (lostId == 0) {
			return;
		}
		Player source = Context.getPlayerServer().getByPlayerId(winId);
		Player target = Context.getPlayerServer().getByPlayerId(lostId);

		if (lostId > 0 && !awardMap.containsKey(lostId)) {
			sendAward(lostId, camType * 2);
		}
		if (camType == 1 && !awardMap.containsKey(winId)) {
			sendAward(winId, camType);
		}

		ChatMain.systemMessage(LanguageLib.get("war.pro.chat" + camType, source.getPlayerName(), target.getPlayerName()));

		Iterator<WarBet> it = bets.iterator();
		while (it.hasNext()) {
			WarBet warBet = (WarBet) it.next();
			if (warBet.getW() > 0 || warBet.getC() != camType * 2) {
				continue;
			}
			if (warBet.getT() != winId && warBet.getT() != lostId) {
				continue;
			}
			Player player = Context.getPlayerServer().getByPlayerId(warBet.getS());

			if (player == null) {
				continue;
			}

			if (warBet.getT() == winId) {
				warBet.setW(1);
				player.alterAsset(warBet.getAsset() * 2);
				Context.getPlayerServer().change(PlayerChangeVO.PLAYER_ASSET, player.getAsset(), warBet.getS());
				player.commitSync();
				LogServer.recordLog(player, ConstantsLog.activity, 90302, warBet.getAsset() * 2, winId);
			} else if (warBet.getT() == lostId) {
				warBet.setW(2);
			}
			String title = LanguageLib.get("war.bet.title");
			String content = LanguageLib.get(warBet.getW() == 1 ? "war.bet.suc" : "war.bet.fail", source.getPlayerName(), target.getPlayerName());
			Context.getMessageServer().recordEvent(player.getPlayerId(), title, content, Constants.eventTypeWarBet, 5);
		}

	}

	private void assignPromotion(Map<Integer, Long> winer) {

		List<Long[]> against = new LinkedList<Long[]>();

		int maxSize = winer.size() / 2;
		maxSize = camType / 2;
		int orderId = 0;
		for (int i = 0; i < maxSize; i++) {
			orderId++;
			int numFirst = i * 2 + 1;
			int numScond = i * 2 + 2;
			if (winer.containsKey(numFirst) && winer.containsKey(numScond)) {
				against.add(new Long[] { winer.get(numFirst), winer.get(numScond) });
			} else {
				if (winer.containsKey(numFirst)) {
					proNext(maxSize, winer.get(numFirst), orderId, 0);
					LogUtil.getMain().debug("fast to promotion " + winer.get(numFirst));
				}
			}
		}

		LogUtil.getMain().debug("against" + against.size() + " " + orderId);
		if (against.size() > 0) {
			for (int i = 0; i < 5; i++) {
				for (Long[] longs : against) {
					WarVs vs = new WarVs();
					vs.setS(longs[0]);
					vs.setT(longs[1]);
					vs.setC(camType);
					vs.setO(i);
					orderVsList.add(vs);
				}
			}
		}
		bet = true;
		LogUtil.getMain().debug("orderVsList" + orderVsList.size());
		winTimesMap.clear();
	}

	public boolean prossVs() {
		Iterator<WarVs> list = orderVsList.iterator();
		int num = 0;

		int diff = TimeProcesser.getUnixTime() - WarConfig.getSingleBegin();

		while (list.hasNext()) {
			WarVs warVs = (WarVs) list.next();
			if (warVs.getW() == 0) {
				if (diff < WarConfig.camStartTime.get(warVs.getC()) + warVs.getO() * 1800) {
					return false;
				}
				haveDo = true;
				bet = false;
				num++;
				int win = battle(warVs.getS(), warVs.getT());
				warVs.setW(win);
				checkPromotion(win == 1 ? warVs.getS() : warVs.getT(), win == 1 ? warVs.getT() : warVs.getS());
			} else {

			}
		}
		LogUtil.getMain().debug("pross vs : " + num);
		camType /= 2;
		haveDo = true;
		return true;
	}

	private void checkPromotion(long winId, long lostId) {
		int cTimes = winTimesMap.containsKey(winId) ? winTimesMap.get(winId) : 0;
		cTimes++;
		winTimesMap.put(winId, cTimes);
		if (cTimes == 3) {
			Map<Integer, Long> winMap = winerMap.get(camType);
			for (Map.Entry<Integer, Long> entry : winMap.entrySet()) {
				if (entry.getValue() == winId) {
					proNext(camType / 2, winId, (entry.getKey() + 1) / 2, lostId);
					break;
				}
			}
		}
	}

	private int battle(long sourceId, long targetId) {
		Player source = Context.getPlayerServer().getByPlayerId(sourceId);
		Player target = Context.getPlayerServer().getByPlayerId(targetId);
		if (source == null || target == null) {
			LogUtil.getMain().error("err " + sourceId + " : " + targetId);
			return -1;
		}
		int[] hpMpS = source.blodMagicMemory();
		int[] hpMpT = target.blodMagicMemory();
		Battle battle = new Battle(source.getLikedFighter(), target.getLikedFighter());
		battle.execBattle(Constants.battle_pvpCross);
		battle.clear();
		source.blodMagicMemoryRevert(hpMpS);
		target.blodMagicMemoryRevert(hpMpT);
		return battle.getWiner() == 1 ? 1 : 2;
	}

	public boolean isProm32(long uid) {
		return winerMap.containsKey(32) && winerMap.get(32).containsValue(uid);
	}

	private long getBoss() {
		if (winerMap.containsKey(1) && winerMap.get(1).containsKey(1)) {
			return winerMap.get(1).get(1);
		}
		return 0;
	}

}
